// import "./../../chartjs/Chart.plugins";
import { Component, OnInit, OnDestroy, ViewChild, AfterViewInit, ElementRef } from '@angular/core';
import { Subject } from "rxjs/Subject";
import { Observable } from "rxjs/Observable";
import { Subscription } from "rxjs/Subscription";
import { CmHttp } from "./../../http-module/http.service";
import { CHART_COLORS, CHART_OPTIONS } from "./fund-flow.chart"
import { Action } from "@ngrx/store";
import { SocketService } from "socket";
import { BASEURL, STOCKURL } from "shared";
import { getId, StockKData, StockDetail } from "utils";
import { SummaryAssistantService, SummaryWithTrading, SUMMARY_STATUS, STOCK_STATUS } from "summary-assistant";
import * as moment from "moment";
import * as OriginChart from "chart.js";
import * as _ from "lodash";
import { KSOCKET_MAP } from "kchart";
import { compare } from "../../lib/tschart/src/lib/index"

// 配置Chart
import { OptimizeUnitPipe } from "./../../pipes/optimize-unit.pipe";
const MAIN_COLOR = "#FF7B00";

let unitOptimize = new OptimizeUnitPipe();
let fillStyle = document.createElement("canvas").getContext("2d").createLinearGradient(0, 0, 0, 300);
fillStyle.addColorStop(1, MAIN_COLOR);
fillStyle.addColorStop(0.8, "rgba(255,123,0,0.3)");
fillStyle.addColorStop(0, "rgba(255,123,0,0)");

const TOGGLE_DATA_STATUS = "TOGGLE_DATA_STATUS";
const RESET_DATA_STATUS = "TOGGLE_DATA_STATUS";
const UPDATE_ORIGIN_DATA = "UPDATE_ORIGIN_DATA";

const LINE_AMOUNT_1DAY = 246; // 240min / 5min + 1 
// const LINE_AMOUNT_1DAY = 49; // 240min / 5min + 1 
const LINE_AMOUNT_7DAY = 246; // 5d * 24h * 60min / 30min

// const getStartTime = (part, tab) =>
//   part.offsets.reduce(
//     (start, offset) =>
//       start[offset.unit](offset.amount),
//     tab.$today ? moment().startOf(tab["$startUnit"]) : moment())
// const getLabelsFromTab = tab => {
//   return tab.$parts
//     .map(part => getStartTime(part, tab))
//     .map(startTime =>
//       new Array(tab["$validPoints"] / tab.$parts.length).fill(1).
//         map((pos, i) =>
//           i === 0 ?
//             startTime.format(tab.$labelFormat) :
//             startTime.add(tab["$labelPadding"], tab["$labelUnit"]).format(tab.$labelFormat)))
//     .reduce((pre, next) => pre.concat(next))
// }

export const LINES_PER_DAY = 300;

const TS_TYPE_NORMAL = 0; // 普通数据
const TS_TYPE_WITH_EMPTY = 1; // 为当日添加空数组，主要用于集合竞价阶段
const TS_TYPE_WITH_TAIL = 2; // 为当日添加前一交易日模拟数据，主要用于交易日停牌阶段股票
const GROUPED_DATE_FORMAT = "YYYYMMDD";
const replaceToToday = time => moment(time).format("YYYY-MM-DD HH:mm:ss").replace(/\d{4}-\d{2}-\d{2}(?=\s)/, moment().format("YYYY-MM-DD"))
const getGroupedTsData = (rawData: StockKData[], duration, tsType) => {
  // 按日期数列分组
  let grouped = _.groupBy(rawData, e => moment(e.time).format(GROUPED_DATE_FORMAT));
  // { 20080202: [ts,ts,ts], 20080303: [ts,ts,ts]}
  let today = moment().format(GROUPED_DATE_FORMAT);
  switch (tsType) {
    case TS_TYPE_NORMAL:
      break;
    case TS_TYPE_WITH_EMPTY:
      // 如果还未有今天数据，补充空数列给今天
      if (grouped[today] === undefined) {
        grouped[today] = [];
      }
      break;
    case TS_TYPE_WITH_TAIL:
      if (grouped[today] === undefined) {
        grouped[today] =
          // 按日期排列数据，并取最后一天数据数组
          grouped[Object.keys(grouped).sort().slice(-1)[0]]
            // 映射最后一天的数据，其中每一数据集均替换为最后一天最后一个数据，仅保留时间
            .map((kData, i, arr) => Object.assign({}, arr.slice(-1)[0], { time: replaceToToday(kData.time) }))
      }
      break;
  }
  // 依据时间排序，取当前周期，并加一天的数据为最终数据
  return Object.keys(grouped).sort().map(date => grouped[date]).slice(-1 * duration);
}

/**
 * 画K线时候的一个数据节点
 * 
 * @export
 * @interface KData
 */
export interface KData {
  time: string | Date;
  open: number;
  high: number;
  low: number;
  close: number;
  volume: number;
  balance: number;
  fundflow: number[];
}

@Component({
  // moduleId: module.id,
  selector: 'fund-flow',
  templateUrl: 'fund-flow.component.html',
  styleUrls: [
    'fund-flow.component.less'
  ],
  inputs: [
    "stockSource"
  ]
})
export class FundFlowComponent implements OnInit, AfterViewInit, OnDestroy {
  @ViewChild("chart", { read: ElementRef })
  chartInstance: ElementRef;
  currentStock;
  stockSource: Observable<any>;
  chartSubscription: Subscription;
  periodSource = new Subject();
  dataStatus;
  stateSource = new Subject();
  stateStore: Observable<any>;
  chart;
  chartColors = CHART_COLORS;
  chartOptions = CHART_OPTIONS;
  currentCate;
  chartLabels;
  originData;
  chartData = [];
  mergedData: number[];
  puredData: number[];
  sumedPuredData: number;
  absSumPuredData: number;
  groupedData: { in: number[], out: number[] };

  tabList = [
    {
      text: "今日",
      $period: "Min1",
      $duration: 1,
      $lastNPoints: LINE_AMOUNT_1DAY,
      $labelPadding: 5,
      $labelUnit: "m",
      $startUnit: "day",
      $parts: [
        {
          offsets: [{
            unit: "hour",
            amount: 9
          }, {
            unit: "minute",
            amount: 30
          }],
        }, {
          offsets: [{
            unit: "hour",
            amount: 13
          }]
        }
      ],
      $labelFormat: "HH:mm",
      $today: true
    }, {
      text: "5日",
      $period: "Min1",
      $duration: 5,
      $lastNPoints: LINE_AMOUNT_1DAY * 5,
      $labelPadding: 30,
      $labelUnit: "m",
      $parts: [
        {
          offsets: [{
            unit: "day",
            amount: -7
          }],
        },
      ],
      $labelFormat: "MM-DD HH:mm",
    }, {
      text: "10日",
      $period: "Min1",
      $duration: 10,
      $lastNPoints: LINE_AMOUNT_1DAY * 10,
      $labelPadding: 30,
      $labelUnit: "m",
      $parts: [
        {
          offsets: [{
            unit: "day",
            amount: -14
          }],
        },
      ],
      $labelFormat: "MM-DD HH:mm",
    }
  ];

  constructor(
    private http: CmHttp,
    private socket: SocketService,
    private summaryService: SummaryAssistantService) {
    // this.stateStore.scan((state, action) => {
    //   switch (action.type) {
    //     case "UPDATE_TAB":
    //       return Object.assign({}, state, {

    //       });
    //     default:
    //       return state;
    //   }
    // })

  }

  ngAfterViewInit() {
    let { http, stockSource, periodSource, summaryService, chartInstance } = this;
    this.chart = new OriginChart(chartInstance.nativeElement, {
      type: 'line',
      data: {
        labels: [],
        datasets: [{
          label: '',
          data: [],
          backgroundColor: fillStyle,
        }]
      },
      options: {
        legend: {
          display: false
        },
        maintainAspectRatio: false,
        scales: {
          xAxes: [{
            // type: "linear",
            ticks: {
              maxRotation: 0,
              autoSkip: false,
              callback: (label: string, index, all) => {
                if (label == "11:30") return `${label}`;
                return index === 0 || index === all.length - 1 ? label : null;
              }
            }
          }],
          yAxes: [{
            ticks: {
              beginAtZero: false,
              // stepSize: 5000000,
              callback: (label, index) => {
                if (Math.abs(label).toString().length >= 9) {
                  return unitOptimize.transform(label, 10000, 0, 2);
                }
                return unitOptimize.transform(label, 10000, 0, 0);
              }
            }
          }]
        },
        elements: {
          point: {
            enabled: false,
            radius: 0
          },
          line: {
            borderWidth: 1,
            borderColor: "#FF7B00",
            fill: "bottom",
          }
        },
        animation: {
          duration: 0
        },
        tooltips: {
          displayColors: false
        },
        alwaysLable: false,
        backgroundColor: fillStyle,
        borderColor: MAIN_COLOR,
        pointBackgroundColor: MAIN_COLOR,
        pointBorderColor: MAIN_COLOR,
        pointHoverBackgroundColor: MAIN_COLOR,
        pointHoverBorderColor: 'rgba(148,159,177,0.8)'
      }
    });
    this.chartSubscription =
      periodSource.subscribe(tab => {
        this.currentCate = tab;
      });
    let fullStock = this.stockSource.
      merge(periodSource).
      scan((preStockWithPeriod, currentOne, index) =>
        Object.assign({}, preStockWithPeriod, currentOne)).
      filter(stock => stock && !!stock.symbol && stock.$period && this.currentCate).publishReplay();
    fullStock.connect();
    this.chartSubscription.add(
      fullStock.subscribe(stock => {
        if (
          this.currentStock &&
          (this.currentStock.symbol !== stock["symbol"] || this.currentStock.$period !== stock["$period"])
        ) {
          this.socket.emit("unsub", { interval: KSOCKET_MAP[this.currentStock.$period], symbol: this.currentStock.symbol });
        }

        this.socket.emit("sub", { interval: KSOCKET_MAP[stock["$period"]], symbol: stock["symbol"] });
        this.currentStock = stock;
      })
    );
    this.chartSubscription.add(fullStock.
      switchMap(stock =>
        Observable.zip(
          http.get(`${STOCKURL}/api/v2/sc/charts/${stock["symbol"]}/kline?period=${stock["$period"]}&lastNPoints=${stock["$lastNPoints"]}`),
          summaryService.getSummaryBySymbol(stock.symbol),
          (kDataList: StockKData[], summary) => {
            let data = kDataList.data.sort(compare('time'))
            switch (summary.stockStatus) {
              case STOCK_STATUS.NORMAL:
                return getGroupedTsData(data, stock.$duration, TS_TYPE_NORMAL);
              case STOCK_STATUS.OCALL:
                return getGroupedTsData(data, stock.$duration, TS_TYPE_WITH_EMPTY);
              case STOCK_STATUS.STOP_AND_TRADING_DAY:
                return getGroupedTsData(data, stock.$duration, TS_TYPE_WITH_TAIL);
              default:
                return data;
            }
          }
        )
      )
      .map(kData => _.flatten(kData))
      .subscribe((kData: any[]) => {
        this.updateChart(kData);
      })
    );
    //默认开启今日
    this.periodSource.next(this.tabList[0]);
    this.socket.on("KChart", kData => {
      let k = Object.keys(kData).map(key => kData[key]).reduce((acc, first) => first, {});
      if (
        k["symbol"] !== this.currentStock["symbol"] ||
        k["interval"] !== KSOCKET_MAP[this.currentStock["$period"]]
      ) return;
      this.updateChart(this.originData, k);
    });
  }

  public updateChart(kDataArray, appendData?) {
    if (!kDataArray || !kDataArray.length) return;
    let originData = appendData ?
      kDataArray.slice(-1)[0]["time"] !== appendData["time"]
        ? [...kDataArray, appendData]
        : [...kDataArray.slice(0, -1), appendData]
      : kDataArray;
    // console.debug("Chart Fund Factory: ", originData, appendData);
    // if (appendData) {
    //   originData = originData.map(k => k["time"] === appendData["time"] ? appendData : k);
    // }

    this.originData = originData;
    let after = originData.map((kd, i, all) => {
      // console.debug("Fund Flow: ", kd);
      // let k = all.slice(0, i + 1).reduce((acc, cur) => {
      //   return {
      //     time: cur.time,
      //     fundflow: acc.fundflow ? cur.fundflow.map((f, fi) => f + acc.fundflow[fi]) : cur.fundflow
      //   }
      // }, {});
      return {
        x: kd.time,
        y: kd.fundflow.reduce((acc, cur, i) => i % 2 === 0 ? acc + cur : acc - cur)
      }
    });
    this.chartLabels = after.map(point => moment(point.x).format(this.currentCate.$labelFormat));
    this.chartData = after.map(point => point.y).map((v, i, a) => a.slice(0, i).reduce((acc, cur) => acc + cur, v));
    this.chart.data.datasets[0].data = this.chartData;
    this.chart.data.labels = this.chartLabels;
    this.chart.update();
  }

  ngOnInit() {

  }

  ngOnDestroy() {
    if (this.chartSubscription) {
      this.chartSubscription.unsubscribe();
    }
    if (this.currentStock && this.currentStock.$period) {
      this.socket.emit("unsub", { interval: KSOCKET_MAP[this.currentStock.$period], symbol: this.currentStock.symbol });
    }
  }

  public setPeriod(period: { $period: string, $lastNPoint: number }) {
    // console.debug("Fund Flow Chart Set Period: ", period);
    this.periodSource.next(period);
  }

  // events
  public chartClicked(e: any): void {
    // console.log(e);
  }

  public chartHovered(e: any): void {
    // console.log(e);
  }

}